import java.util.*;
class Point
{
	float _xc;
	float _yc;
	Point(float xc,float yc)
	{
		_xc=xc;
		_yc=yc;
	}
}
class SeriesOptions
{
	int _argumentType;
	int _seriesMode;
	static final int TEMPERATURE=0;
	static final int MAGNETICFIELD=1;
	static final int TIME=2;
	static final int HEATBATH=0;
	static final int METROPOLIS=1;
	static final int DEMON=2;
	static final int CONSTANT=0;
	static final int LINEAR=1;
	static final int EXPONENTIAL=2;
	static final int SINE=3;
	static final int LOGARITHMIC=4;
	float _start;
	float _step;
	float _end;
	float _frequency;
	float _phase;
	float _auxillary;
	int _algoritme;
	boolean _asymptotic;
	boolean _freeze;
	SeriesOptions()
	{
		_freeze=false;
		_start=10;
		_step=10;
		_end=100;
		_frequency=0;
		_phase=10;
		_auxillary=100;
		_argumentType=TEMPERATURE;
		_seriesMode=LINEAR;
		_algoritme=HEATBATH;
		_asymptotic=true;
	}
}

class Series
{
	private Engine _engines[];
	private Engine _heatbath;
	public SeriesOptions _options;
	private LinkedList<ConfPoint> _points;
	private float _maxTemperature;
	private float _minTemperature;
	private float _maxField;
	private float _minField;
	private long _maxTime;
	private long _minTime;
	private long _maxSpin;
	private long _minSpin;
	private boolean _finished;
	Series(Engine engines[])
	{
		_engines=engines;
		_points=new LinkedList<ConfPoint>();
		_finished=false;
		_options=new SeriesOptions();
		_heatbath=null;
	}
	void calculate()
	{
		_heatbath=_engines[_options._algoritme];
		if(_options._asymptotic)
		{
			switch(_options._argumentType)
			{
				case SeriesOptions.TEMPERATURE: measureSpinTemperature(_options._start,_options._step,_options._end,_options._auxillary,_options._seriesMode);
					break;
				case SeriesOptions.MAGNETICFIELD: measureSpinMagneticField(_options._start,_options._step,_options._end,_options._auxillary,_options._seriesMode);
					break;
				case SeriesOptions.TIME:
					break;
			}
		}
	}
	void measureSpinTemperature(float start,float step,float end,float magneticfield,int mode)
	{
		_finished=false;
		float temperature=start;
		_heatbath.setMagneticField(magneticfield);
		_points.clear();
		ConfPoint cp=null;
		while(temperature<end)
		{
			_heatbath.setTemperature(temperature);
			_heatbath.step();
			cp=_heatbath.getPoint();
			_points.add(cp);
			if(mode==SeriesOptions.LINEAR)
			temperature+=step;
			else if(mode==SeriesOptions.LOGARITHMIC)
			temperature*=step;
		}
		_finished=true;
	}
	void measureSpinMagneticField(float start,float step,float end,float temperature,int mode)
	{
		_finished=false;
		_heatbath.setTemperature(temperature);
		float magneticfield=start;
		_points.clear();
		ConfPoint cp=null;
		while(magneticfield<end)
		{
			_heatbath.setMagneticField(magneticfield);
			cp=_heatbath.getPoint();
			_points.add(cp);
			if(mode==SeriesOptions.LINEAR)
			magneticfield+=step;
			else if(mode==SeriesOptions.LOGARITHMIC)
			magneticfield*=step;
		}
		_finished=true;
	}
	void plot2D(LinkedList<Point> lst,int mode)
	{
		ListIterator<ConfPoint> cpt=_points.listIterator();
		lst.clear();
		while(cpt.hasNext())
		{
			ConfPoint p=cpt.next();
			switch(mode)
			{
				case 0: lst.addLast(new Point(p._temperature,p._spin));
					break;
				case 1: lst.addLast(new Point(p._magneticField,p._spin));
					break;
				case 2: lst.addLast(new Point(p._time,p._spin));
					break;
			}
		}
	}
	void differentiate()
	{
		ListIterator<ConfPoint> cpt=_points.listIterator();
		ConfPoint curr,prev;
		LinkedList<ConfPoint> derivative=new LinkedList<ConfPoint>();
		prev=curr=null;
		while(cpt.hasNext())
		{
			curr=cpt.next();
			if(prev!=null)
			{
				ConfPoint p=new ConfPoint((curr._spin-prev._spin)/2,(curr._temperature+prev._temperature)/2,(curr._magneticField+prev._magneticField)/2,(curr._time+prev._time)/2);
				derivative.add(p);
			}
			prev=curr;
		}
		_points=derivative;
	}
	void getLimits()
	{
		ListIterator<ConfPoint> cpt=_points.listIterator();
		while(cpt.hasNext())
		{
			ConfPoint p=cpt.next();
			if(p._temperature>_maxTemperature) _maxTemperature=p._temperature;
			if(p._temperature<_minTemperature) _minTemperature=p._temperature;
			if(p._magneticField>_maxField) _maxField=p._magneticField;
			if(p._magneticField<_minField) _minField=p._magneticField;
			if(p._spin>_maxSpin) _maxSpin=p._spin;
			if(p._spin<_minSpin) _minSpin=p._spin;
			if(p._time>_maxTime) _maxTime=p._time;
			if(p._time<_minTime) _minTime=p._time;
		}
	}
	void initSeries(int length,int width)
	{
		Lattice lattice=new Lattice(length,width);
		_engines=new Engine[1];
		_engines[0]=new HeatBathEngine(lattice);
	}
}
